package cn.lg.soar.mvc.handler;

import cn.lg.soar.common.util.EnumUtils;
import cn.lg.soar.common.util.data.DataUtil;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.multipart.support.MultipartResolutionDelegate;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.*;
import java.util.stream.Collectors;

public class CollectionTypeArgumentResolver implements HandlerMethodArgumentResolver {
 
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Collection.class.isAssignableFrom(parameter.getParameterType()) && parameter.getParameterType().isInterface();
    }
 
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws ServletException, IOException, NoSuchMethodException, IllegalAccessException {
        String parameterName = parameter.getParameterName();
        Class<?> parameterType = parameter.getParameterType();
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        // Regular Map
        Class<?> valueType = resolvableType.asCollection().getGeneric(0).resolve();
        if (Set.class.isAssignableFrom(parameterType)) {
            if (valueType == MultipartFile.class) {
                MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
                return (multipartRequest != null ? new HashSet<>(multipartRequest.getFiles(parameterName)) : new HashSet<>(0));
            } else if (valueType == Part.class) {
                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
                if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
                    return new HashSet<>(servletRequest.getParts());
                }
                return new HashSet<>(0);
            } else if (Enum.class.isAssignableFrom(valueType)) {
                String[] parameterValues = webRequest.getParameterValues(parameterName);
                if (parameterValues == null) {
                    return new HashSet<>(0);
                }
                if (parameterValues.length == 1) {
                    parameterValues = parameterValues[0].split(",");
                }
                // 调用枚举原生方法获取
                MethodType methodType = MethodType.methodType(valueType, String.class);
                MethodHandles.Lookup lookup = MethodHandles.lookup();
                MethodHandle methodHandle = lookup.findStatic(valueType, "valueOf", methodType);

                return Arrays.stream(parameterValues)
                        .map(x -> {
                            try {
                                return methodHandle.invoke(x);
                            } catch (Throwable e) {
                                throw new RuntimeException(e);
                            }
                        })
                        .collect(Collectors.toSet());
            } else {
                String[] parameterValues = webRequest.getParameterValues(parameterName);
                if (parameterValues == null) {
                    return new HashSet<>(0);
                }
                if (parameterValues.length == 1) {
                    parameterValues = parameterValues[0].split(",");
                }
                return Arrays.stream(parameterValues)
                        .map(x -> DataUtil.to(valueType, x))
                        .collect(Collectors.toSet());
            }
        }

        if (valueType == MultipartFile.class) {
            MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
            return (multipartRequest != null ? multipartRequest.getFiles(parameterName) : new ArrayList<>(0));
        } else if (valueType == Part.class) {
            HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
                return new ArrayList<>(servletRequest.getParts());
            }
            return new ArrayList<>(0);
        } else if (Enum.class.isAssignableFrom(valueType)) {
            String[] parameterValues = webRequest.getParameterValues(parameterName);
            if (parameterValues == null) {
                return new HashSet<>(0);
            }
            if (parameterValues.length == 1) {
                parameterValues = parameterValues[0].split(",");
            }
            // 调用枚举原生方法获取
            MethodType methodType = MethodType.methodType(valueType, String.class);
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle methodHandle = lookup.findStatic(valueType, "valueOf", methodType);

            return Arrays.stream(parameterValues)
                    .map(x -> {
                        try {
                            return methodHandle.invoke(x);
                        } catch (Throwable e) {
                            throw new RuntimeException(e);
                        }
                    })
                    .collect(Collectors.toList());
        } else {
            String[] parameterValues = webRequest.getParameterValues(parameterName);
            if (parameterValues == null) {
                return new ArrayList<>(0);
            }
            if (parameterValues.length == 1) {
                parameterValues = parameterValues[0].split(",");
            }
            return Arrays.stream(parameterValues)
                    .map(x -> DataUtil.to(valueType, x))
                    .collect(Collectors.toList());
        }
    }
}